""" PsyBlockGame settings module """

if __name__ == '__main__':
    import os
    import sys
    PATH_DIRS = [
        r'\bin\lib',
        r'\bin\lib\site-packages',
        r'\src',
    ]
    sys.path = [os.getcwd() + path for path in PATH_DIRS]

from PyQt4.QtGui import *
from PyQt4.QtCore import SIGNAL
from PsyBlockGame.Game import GameConfig, FigureConfig, Game
from PsyBlockGame.UI.Settings import Ui_Settings
from PsyBlockGame.FigureSettings import FigureSettings
from PsyBlockGame.ApplicationConfig import ApplicationConfig

DEFAULT_FIGURES = [
    ('Square',      QColor("Tomato"),   'A'),
    ('Circle',      QColor("Orange"),   'S'),
    ('Rhomb',       QColor("DarkBlue"), 'D'),
    ('Triangle',    QColor("Green"),    'F'),
]
DEFAULT_FIGURES = [FigureConfig(type, color, key) for type, color, key in DEFAULT_FIGURES]

for index, p in enumerate(DEFAULT_FIGURES):
    QColorDialog.setCustomColor(index, p.color.rgb())

class GameSettings(QDialog):
    def __init__(self, appConfig, parent = None):
        super(GameSettings, self).__init__(parent)
        self.ui = Ui_Settings()
        self.ui.setupUi(self)

        self.isInitData = False

        self.appConfig = appConfig
        self.config = appConfig.currentConfig
        self.updateKeyButtons()

        self.initPreview()

        self.initData()
        self.updateFigureWidgets()

        self.startTimer(0)

    def initData(self):   
        # init preview widgets
        self.previewWidgets = []
        for k in range(self.ui.numberOfFigures.maximum()):
            w = FigureSettings(DEFAULT_FIGURES[k].copy())
            w.setVisible(False)
            self.previewWidgets.append(w)
            self.ui.figureSettingList.addWidget(w)
            
            self.connect(w, SIGNAL('dataChanged()'), self.onFigureChanged)
            self.connect(w, SIGNAL('figureKeyChanged(QWidget *)'), self.onFigureKeyChanged)
            
        self.ui.figureSettingList.addStretch()

        self.updateElementsByData()

        self.isInitData = True
        # config set
        for name in self.appConfig.namedConfigs:
            self.ui.settingsSet.addItem(name)
        if len(self.appConfig.lastName) > 0:
            index = self.ui.settingsSet.findText(self.appConfig.lastName)
            self.ui.settingsSet.setCurrentIndex(index)
        else:
            self.ui.settingsSet.setCurrentIndex(-1)
        self.isInitData = False

    def initPreview(self):
        self.ui.motionPreview.setRenderHint(QPainter.Antialiasing)
        self.motionPreviewGame = Game(self.ui.motionPreview)
        self.motionPreviewGame.config = self.config
        self.motionPreviewGame.startPreviewMotion()

        self.ui.roundPreview.setRenderHint(QPainter.Antialiasing)
        self.roundPreviewGame = Game(self.ui.roundPreview)
        self.roundPreviewGame.config = self.config
        self.roundPreviewGame.startLevel()
        self.connect(self.roundPreviewGame, SIGNAL("onFlyAwayTimeout()"), self.onRoundPreviewTimeout)

    def updateKeyButtons(self):
        emptyFigures = [p for p in self.config.figures if len(p.key) == 0]
        usedKeys = { p.key for p in self.config.figures if len(p.key) > 0 }
        defKeys = [p.key for p in DEFAULT_FIGURES if p.key not in usedKeys]

        for p in emptyFigures:
            if len(p.key) == 0 or p.key in usedKeys:
                p.key = defKeys[0]
                del defKeys[0]
                self.onDataChanged()
            else:
                defKeys = [key for key in defKeys if key != p.key]
                usedKeys.add(p.key)

    #
    # UI callbacks
    #
    def timerEvent(self, e):
        self.motionPreviewGame.update()
        self.roundPreviewGame.update()

    def onDataChanged(self):
        if not self.isInitData:
            self.appConfig.isConfigModified = True

    def onNumberOfFiguresChanged(self, newCount):
        count = len(self.config.figures)
        if newCount > count:
            for k in range(newCount - count):
                index = count + k
                assert not self.previewWidgets[index].isVisible()
                self.config.figures.append(self.previewWidgets[index].config)
        else:
            self.config.figures[newCount:count] = []

        self.updateFigureWidgets()

    def onFigureWidthChanged(self, width):
        self.config.figureWidth = width
        self.updateFigureWidgets()

    def onFigureBorderChanged(self, width):
        self.config.figureBorderWidth = width
        self.updateFigureWidgets()

    def onFigureChanged(self):
        self.appConfig.isConfigModified = True
        self.updateFigureWidgets()

    def onFigureKeyChanged(self, figure):
        for p in self.config.figures:
            if p is not figure.config and p.key == figure.config.key:
                p.key = ''
        self.updateKeyButtons()

    def onBodyColorChange(self):
        color = QColorDialog.getColor(self.config.figureBackColor, self)
        if not color.isValid():
            return

        self.appConfig.isConfigModified = True
        
        self.config.figureBackColor = color
        self.updateBodyColorBtn()
        self.updateFigureWidgets()

    def onSave(self):
        name = self.ui.settingsSet.currentText()
        if len(name) == 0:
            QMessageBox.warning(self, self.tr("Cannot save"), self.tr("Please enter a preset name"))
            return

        self.appConfig.lastName = name
        if name not in self.appConfig.namedConfigs:
            self.ui.settingsSet.addItem(name)
        self.appConfig.isConfigModified = False
        self.appConfig.namedConfigs[name] = self.appConfig.currentConfig.copy()
        self.appConfig.save()

    def onDelete(self):
        name = self.ui.settingsSet.currentText()
        if name not in self.appConfig.namedConfigs:
            return

        res = QMessageBox.question(
                self,
                self.tr("Delete"),
                self.tr("Ar you sure you want to delete the preset '%s'?") % name,
                QMessageBox.Yes | QMessageBox.No)

        if res != QMessageBox.Yes:
            return

        del self.appConfig.namedConfigs[name]

        self.isInitData = True
        index = self.ui.settingsSet.findText(name)
        if index != -1:
            self.ui.settingsSet.removeItem(index)
        self.ui.settingsSet.setCurrentIndex(-1)
        self.isInitData = False

        self.appConfig.lastName = ""
        self.setConfig(GameConfig())

    def onSetChanged(self, name):
        if self.isInitData:
            return

        if len(name) == 0:
            self.appConfig.lastName = name
            self.setConfig(GameConfig())
            return

        if name not in self.appConfig.namedConfigs:
            self.appConfig.namedConfigs[name] = self.appConfig.currentConfig.copy()
            self.appConfig.lastName = name
            self.appConfig.save()
            return

        if not self.checkChangedData():
            self.isInitData = True
            index = self.ui.settingsSet.findText(self.appConfig.lastName)
            self.ui.settingsSet.setCurrentIndex(index)
            self.isInitData = False
            return

        self.appConfig.lastName = name
        self.setConfig(self.appConfig.namedConfigs[name].copy())

    def checkChangedData(self):
        if not self.appConfig.isConfigModified:
            return True

        if len(self.appConfig.lastName) > 0:
            res = QMessageBox.warning(
                self, self.tr("Save?"),
                self.tr("Do you want to save your changes of preset '%s'?") % self.appConfig.lastName,
                QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)

            if res == QMessageBox.Yes:
                self.appConfig.namedConfigs[self.appConfig.lastName] = self.appConfig.currentConfig.copy()
            elif res == QMessageBox.Cancel:
                return False
        else:
            res = QMessageBox.warning(
                self, self.tr("Ignore changes?"),
                self.tr("Do you want to ignore your previous changes?"),
                QMessageBox.Yes | QMessageBox.No)

            if res == QMessageBox.No:
                return False

        return True

    def onRotationRadiusChanged(self, radius):
        if not self.isInitData:
            self.config.rotationRadius = radius

    def onRotationSpeedChanged(self, speed):
        if not self.isInitData:
            self.motionPreviewGame.restartTimer()
            self.config.rotationSpeed = speed
            if self.lockFigureRotation:
                self.config.eachFigureRotationSpeed = speed
                self.isInitData = True
                self.ui.figureRotationSpeed.setValue(self.config.eachFigureRotationSpeed)
                self.isInitData = False

    def onFigureRotationSpeedChanged(self, speed):
        if not self.isInitData:
            self.motionPreviewGame.restartTimer()
            self.config.eachFigureRotationSpeed = speed

    def onRotationSpeedLockChanged(self, lock):
        self.lockFigureRotation = lock

        if not self.isInitData and lock:
            self.motionPreviewGame.restartTimer()
            self.config.eachFigureRotationSpeed = self.config.rotationSpeed
            self.isInitData = True
            self.ui.figureRotationSpeed.setValue(self.config.eachFigureRotationSpeed)
            self.isInitData = False

    def closeEvent(self, e):
        self.appConfig.save()

    def onGameTimeChanged(self, value):
        if not self.isInitData:
            self.config.gameTime = value

    def onRoundStartTimeChanged(self, value):
        if not self.isInitData:
            self.config.startRoundTime = value

    def onFlyAwayFromChanged(self, value):
        if not self.isInitData:
            self.config.flyAwayTimeFrom = value

            if self.config.flyAwayTimeFrom > self.config.flyAwayTimeTo:
                self.isInitData = True
                self.config.flyAwayTimeTo = self.config.flyAwayTimeFrom
                self.ui.flyAwayTimeTo.setValue(self.config.flyAwayTimeFrom)
                self.isInitData = False

    def onFlyAwayToChanged(self, value):
        if not self.isInitData:
            self.config.flyAwayTimeTo = value

            if self.config.flyAwayTimeTo < self.config.flyAwayTimeFrom:
                self.isInitData = True
                self.config.flyAwayTimeFrom = self.config.flyAwayTimeTo
                self.ui.flyAwayTimeFrom.setValue(self.config.flyAwayTimeTo)
                self.isInitData = False

    def onFlyAwayTimeoutChanged(self, value):
        if not self.isInitData:
            self.config.flyAwayTimeout = value

    def onFlyAwaySpeedChanged(self, speed):
        if not self.isInitData:
            self.config.flyAwaySpeed = speed

    def onRestartRoundPreview(self):
        self.roundPreviewGame.startLevel()

    #
    # Update methods
    #
    def setConfig(self, config):
        self.appConfig.isConfigModified = False
        self.appConfig.currentConfig = config
        self.appConfig.save()
        
        self.config = config
        self.updateKeyButtons()
        self.updateElementsByData()
        self.updateFigureWidgets()

    def updateElementsByData(self):
        self.isInitData = True

        # body color
        self.updateBodyColorBtn()
        # figures count
        self.ui.numberOfFigures.setValue(len(self.config.figures))
        # figure size
        self.ui.figureWidth.setValue(self.config.figureWidth)
        # figure border width
        self.ui.figureBorder.setValue(self.config.figureBorderWidth)

        # rotation radius
        self.ui.rotationRadius.setValue(self.config.rotationRadius)
        # rotation speed
        self.ui.rotationSpeed.setValue(self.config.rotationSpeed)
        # figure rotation speed
        self.ui.figureRotationSpeed.setValue(self.config.eachFigureRotationSpeed)

        if self.config.rotationSpeed == self.config.eachFigureRotationSpeed and self.config.rotationSpeed != 0:
            self.ui.lockWithRotationSpeed.setChecked(True)
            self.lockFigureRotation = True
        else:
            self.ui.lockWithRotationSpeed.setChecked(False)
            self.lockFigureRotation = False

        # game start time
        self.ui.gameTime.setValue(self.config.gameTime)
        # round start time
        self.ui.roundStartTime.setValue(self.config.startRoundTime)
        # fly away time
        self.ui.flyAwayTimeFrom.setValue(self.config.flyAwayTimeFrom)
        self.ui.flyAwayTimeTo.setValue(self.config.flyAwayTimeTo)
        # fly away timeout
        self.ui.flyAwayTimeout.setValue(self.config.flyAwayTimeout)
        # fly away speed
        self.ui.flyAwaySpeed.setValue(self.config.flyAwaySpeed)

        self.isInitData = False

    def updateFigureWidgets(self):
        count = len(self.config.figures)
        width = 0
        for index, w in enumerate(self.previewWidgets):
            if index >= count:
                w.setVisible(False)
                continue

            w.setVisible(True)
            w.setConfig(self.config.figures[index])
            w.updatePreview(self.config)
            sx, sy = w.getPreviewSize()
            width = max(width, sx)

        for index in range(count):
            w = self.previewWidgets[index]
            sx, sy = w.getPreviewSize()
            w.setPreviewSize(width, sy)

        self.motionPreviewGame.config = self.config
        self.motionPreviewGame.startPreviewMotion()

        self.roundPreviewGame.config = self.config
        self.roundPreviewGame.startLevel()
        self.ui.roundPreview.setSceneRect(self.roundPreviewGame.scene.itemsBoundingRect())

    def updateBodyColorBtn(self):
        p = QPalette()
        p.setBrush(QPalette.Button, QBrush(self.config.figureBackColor))
        self.ui.bodyColorBtn.setPalette(p)

    def onRoundPreviewTimeout(self):
        self.roundPreviewGame.startRound()

if __name__ == '__main__':
    qApp = QApplication(sys.argv)
    config = ApplicationConfig()
    config.load()
    wnd = GameSettings(config)
    sys.exit(wnd.exec_())